package com.dnb.webmash.playmaker.server;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import com.dnb.webmash.playmaker.shared.MediaType;

//manages hardcoded mediatypes and user entered mediatypes
public class MediaLibrary {
	private static final Logger LOG = Logger.getLogger(WebPlaylistServiceImpl.class.getName()); //TODO USE IF NECESSARY FOR APP ENGINE SERVERSIDE LOGGING (viewable online)
	
	public static ArrayList<MediaType> getGenericTypes() {
		ArrayList<MediaType> result = new ArrayList<MediaType>(MediaLibrary.getCodedMediaTypes("com.dnb.webmash.playmaker.shared.generics"));
		return result;		
	}
	
	public static ArrayList<MediaType> getMediaTypes() {
		ArrayList<MediaType> result = new ArrayList<MediaType>(MediaLibrary.getCodedMediaTypes("com.dnb.webmash.playmaker.shared.mediatypes"));
		return result;		
	}
	public static ArrayList<MediaType> getAllTypes() {
		ArrayList<MediaType> total = new ArrayList<MediaType>(MediaLibrary.getStoredMediaTypes());
		total.addAll(MediaLibrary.getGenericTypes());
		total.addAll(MediaLibrary.getMediaTypes());
		total.trimToSize();
		return total;
	}
	@SuppressWarnings("rawtypes")
	public static ArrayList<MediaType> getCodedMediaTypes(String location){
		ArrayList<MediaType> finalR = new ArrayList<MediaType>();
		ArrayList<Class> result=null;
		try {
			result = new ArrayList<Class>(MediaLibrary.getClasses(location));
		} catch (ClassNotFoundException e1) {
			LOG.log(Level.SEVERE, e1.getLocalizedMessage());
		} catch (IOException e1) {
			LOG.log(Level.SEVERE, e1.getLocalizedMessage());
		}
		
		if (result != null){
		for (int i = 0; i < result.size(); i++){ 
				try {							
					
						//finalR.add(MediaType.class.cast(result.get(i).asSubclass(MediaType.class)));
						finalR.add((MediaType)result.get(i).newInstance());
						
				} catch (InstantiationException e) {
					LOG.log(Level.SEVERE, e.getLocalizedMessage());
				} catch (IllegalAccessException e) {
					LOG.log(Level.SEVERE, e.getLocalizedMessage());
				}
			}
		}
		return finalR;
	}
	
	//returns mediatypes stored in datastore
	@SuppressWarnings("unchecked")
	public static ArrayList<MediaType> getStoredMediaTypes(){ //TODO TEST THIS... GETTING SERIALIZATION ERRORS
		PersistenceManager pm = PMF.get().getPersistenceManager();
	    ArrayList<MediaType> results = new ArrayList<MediaType>();
	    try {
	    	Query query = pm.newQuery(MediaType.class);
	    	List<MediaType> mediatypes = (List<MediaType>) query.execute();
	    	for (MediaType mt : mediatypes){
	    		results.add(mt.cleanArray());
	    	}
	    } finally {
	        pm.close();
	    }
		return results;
	}
	/**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws IOException
     * @author vtatai
     * from http://snippets.dzone.com/posts/show/4831
     */
	@SuppressWarnings("rawtypes")
	private static List<Class> getClasses(String packageName)
            throws ClassNotFoundException, IOException 
    {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            String fileName = resource.getFile();
            String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8");
            dirs.add(new File(fileNameDecoded));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes;
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
	@SuppressWarnings("rawtypes")
	private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException 
	{
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
        	String fileName = file.getName();
            if (file.isDirectory()) {
                assert !fileName.contains(".");
            	classes.addAll(findClasses(file, packageName + "." + fileName));
            } else if (fileName.endsWith(".class") && !fileName.contains("$")) {
            	Class _class;
				try {
					_class = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6));
				} catch (ExceptionInInitializerError e) {
					// happen, for example, in classes, which depend on 
					// Spring to inject some beans, and which fail, 
					// if dependency is not fulfilled
					LOG.log(Level.FINE, e.getLocalizedMessage());
					_class = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6),
							false, Thread.currentThread().getContextClassLoader());
				}
				classes.add(_class);
            }
        }
        return classes;
    }
}
